﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.ETL
{
    /// <summary>
    /// 
    /// </summary>
    public class CopyDbWorker:IWorker 
    {
        #region Implementation of IWorker

        public event WorkerStarted WorkerStartedEvent;
        public event WorkerFinished WorkerFinishedEvent;
        public event WorkerHasError WorkerErredEvent;
        public event WorkerCancelled WorkerCancelledEvent;
        public event WorkerStatusChanged WorkerStatusChangedEvent;

        private Thread workerThread;
        private CopyDbInstruction instruction;
        private CopyDbOutput output;
        private RunContext _Context;
        private Guid _TicketID;
        private string _WorkflowName;
        private string _ComponentName;

        /// <summary>
        /// 
        /// </summary>
        public string ComponentName
        {
            get { return _ComponentName; }
        }

        /// <summary>
        /// 
        /// </summary>
        public string WorkflowName
        {
            get { return _WorkflowName; }
        }

        /// <summary></summary>
        public Thread WorkerThread
        {
            get { return workerThread; }
        }
        /// <summary></summary>
        public InstructionBase Instruction
        {
            get { return instruction; }
        }
        /// <summary></summary>
        public ResultBase Output
        {
            get { return output; }
        }
        /// <summary></summary>
        public Type ComponentType
        {
            get { return typeof(CopyDbComponent); }
        }
        /// <summary></summary>
        public RunContext Context
        {
            get { return _Context; }
            set { _Context = value; }
        }
        /// <summary></summary>
        public void SetInstruction(Guid ticketGuid, InstructionBase request, string workflowName, string componentName)
        {
            this._TicketID = ticketGuid;
            this.instruction = (CopyDbInstruction)request;
            this._WorkflowName = workflowName;
            this._ComponentName = componentName;
        }
        /// <summary></summary>
        public void SetInstruction(RunContext context, Guid ticketGuid, InstructionBase request, string workflowName, string componentName)
        {
            this._TicketID = ticketGuid;
            this.instruction = (CopyDbInstruction)request;
            this._Context = context;
            this._WorkflowName = workflowName;
            this._ComponentName = componentName;
        }
        /// <summary></summary>
        public void DoWork()
        {
            if (this.WorkerStartedEvent != null)
            {
                this.WorkerStartedEvent(this._TicketID, this, this.GetType(), this.instruction);
            }
            this.workerThread = Thread.CurrentThread;
            this.CopyDatabase();
        }
        /// <summary></summary>
        public void DoWorkAsync()
        {
            if (this.WorkerStartedEvent != null)
            {
                this.WorkerStartedEvent(this._TicketID, this, this.GetType(), this.instruction);
            }
            this.workerThread = new Thread(new ThreadStart(this.CopyDatabase));
            this.workerThread.Start();
        }
        /// <summary></summary>
        public void CancelWork()
        {
            if (this.workerThread != null && this.workerThread.IsAlive)
            {
                this.workerThread.Abort();
                this.workerThread.Join();

                if (this.WorkerCancelledEvent != null)
                {
                    this.WorkerCancelledEvent(this._TicketID, this.GetType(), this.instruction);
                }
            }
        }

        #endregion

        #region work
        private void CopyDatabase()
        {
            try
            {
                int tblCount = 0;
                int idxCount = 0;
                int recCount = 0;
                int refCount = 0;
                int tblCopied = 0;

                Dictionary<string, List<string>> tblsBySchemas =
                    this.instruction.TablesToCopy;
                if(tblsBySchemas==null || tblsBySchemas.Count==0)
                    throw new Exception("Unable to find any tables");
                foreach (string schemaName in tblsBySchemas.Keys)
                {
                    tblCount += tblsBySchemas[schemaName].Count;
                }
                if(tblCount==0)
                    throw new Exception("Unable to find any tables");

                // sync tables 
                SyncDbTableWorker syncTableWorker=new SyncDbTableWorker();
                foreach(string schemaName in tblsBySchemas.Keys)
                {
                    foreach(string tblName in tblsBySchemas[schemaName])
                    {
                        if(this.WorkerStatusChangedEvent !=null)
                        {
                            int pct = tblCopied*100/tblCount;
                            string msg = string.Format("Copying {0}.{1}, copied {2} of {3} tables...", schemaName,
                                                       tblName, tblCopied, tblCount);
                            this.WorkerStatusChangedEvent(this._TicketID, this.GetType(), this.instruction,
                                                          this._WorkflowName, this._ComponentName, msg, pct);
                        }


                        SyncDbTableInstruction syncInstruction = new SyncDbTableInstruction(
                            Guid.NewGuid(), Guid.NewGuid(),
                            this.instruction.SrcDbProvider, this.instruction.SrcConnStr,
                            schemaName, tblName, false, string.Empty, new List<string>(),
                            string.Empty, this.instruction.TgtDbProvider, this.instruction.TgtConnStr,
                            tblName, new Dictionary<string, string>());
                        syncTableWorker.SetInstruction(Guid.NewGuid(), syncInstruction, "Sync Table", "Sync " + tblName);
                        syncTableWorker.DoWork();

                        tblCopied++;
                        int tblRecCount = DbUtil.GetTableRecordCount(this.instruction.SrcDbProvider,
                                                                     this.instruction.SrcConnStr, schemaName, tblName);
                        recCount += tblRecCount;
                    }
                }
                // build indexes
                foreach (string schemaName in tblsBySchemas.Keys)
                {
                    foreach (string tblName in tblsBySchemas[schemaName])
                    {
                        string[] indexedColumns = DbUtil.GetTableIndexedColumnNames(
                            this.instruction.SrcDbProvider, this.instruction.SrcConnStr, schemaName, tblName);
                        if(indexedColumns !=null && indexedColumns.Length>0)
                        {
                            foreach(string colName in indexedColumns)
                            {
                                string idxName = string.Format("IX_{0}_{1}", tblName, colName);
                                DbUtil.AddIndex(this.instruction.TgtConnStr,tblName,colName,idxName);
                                idxCount++;
                            }
                        }
                    }
                }
                // build references
                foreach (string schemaName in tblsBySchemas.Keys)
                {
                    foreach (string tblName in tblsBySchemas[schemaName])
                    {
                        List<TableReference> tblRefs = DbUtil.GetTableReference(
                            this.instruction.SrcDbProvider, this.instruction.SrcConnStr,
                            schemaName, tblName);
                        if(tblRefs !=null && tblRefs.Count>0)
                        {
                            foreach(TableReference tblRef in tblRefs)
                            {
                                DbUtil.AddTableForeignKey(this.instruction.TgtConnStr, tblName, tblRef.ChildColumnName,
                                                          tblRef.ParentTableName, tblRef.ParentColumnName);
                                refCount++;
                            }
                        }
                    }
                }

                this.output=new CopyDbOutput(this.instruction.OwnerComponentID,this._TicketID,tblCount,recCount,idxCount,refCount);
                if(this.WorkerFinishedEvent !=null)
                {
                    this.WorkerFinishedEvent(this._TicketID, this.GetType(), this.instruction, this.output);
                }
            }
            catch(Exception ex)
            {
                if (this.WorkerErredEvent != null)
                    this.WorkerErredEvent(this._TicketID, this.GetType(), this.instruction, ex.ToString(), true);
            }
        }
        #endregion
    }
}
